package com.atguigu.gmall.activity.service.impl;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.activity.util.CacheHelper;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.MD5;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class SeckillGoodsServiceImpl implements SeckillGoodsService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    //查询所有 查询redis
    @Override
    public List<SeckillGoods> findAll() {
        List<SeckillGoods> seckillGoodsList = redisTemplate.opsForHash().values(RedisConst.SECKILL_GOODS);
        return seckillGoodsList;
    }
    //根据skuId查询商品详情  查询redis
    @Override
    public SeckillGoods getSeckillGoods(Long skuId) {
        SeckillGoods seckillGoods = (SeckillGoods) this.redisTemplate.opsForHash().get(RedisConst.SECKILL_GOODS, skuId.toString());
        return seckillGoods;
    }

    //预下单
    @Override
    public Result seckillOrder(Long skuId, String skuIdStr,String userId) {
        /**
         * 1.校验抢购码
         * 2.校验状态位
         */
        if (!skuIdStr.equals(MD5.encrypt(userId))){
            return Result.fail().message("校验抢购码失败");
        }
        String state = (String) CacheHelper.get(skuId.toString());
        if ("0".equals(state)){
            //提示信息
            return Result.fail().message("售罄");
        }else if(StringUtils.isEmpty(state)){
            //提示信息
            return Result.fail().message("非法状态");
        }else{
            // 状态为 1 说明这个商品可以秒杀  将商品与用户保存到队列中
            UserRecode userRecode = new UserRecode();
            userRecode.setSkuId(skuId);
            userRecode.setUserId(userId);
            //将对象放入队列中
            this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_SECKILL_USER,MqConst.ROUTING_SECKILL_USER,userRecode);
            //正确返回
            return Result.ok();
        }
    }

    @Override
    public void seckillUserOrder(UserRecode userRecode) {
        /**
         * 1.判断状态位
         * 2.判断用户是否下过订单  每个用户只能购买一次
         *       setnx key value
         * 3.减库存 list队列
         *      rpop key
         *      出队失败，说明没有库存，则通知其他兄弟节点，别秒杀
         *      publish seckillpush skuId:0
         * 4.将用户秒杀的商品信息 保存到redis中
         * 5.更新redis - stockCount，mySql - stockCount(剩余数量)
         */

        String state = (String) CacheHelper.get(userRecode.getSkuId().toString());
        //判断状态位
        if (StringUtils.isEmpty(state)||"0".equals(state)){
            return;
        }
        //为了保障每个用户只能购买一次 使用setnx命令来做判断
        String userKey = RedisConst.SECKILL_USER+userRecode.getUserId();
        Boolean result = this.redisTemplate.opsForValue().setIfAbsent(userKey, userRecode.getSkuId().toString(),RedisConst.SKUKEY_TIMEOUT, TimeUnit.MINUTES);
        //result = true 第一次购买  result=false 已经购买过了
        if (!result){
            return;
        }
        String skuIdExist = (String) this.redisTemplate.opsForList().rightPop(RedisConst.SECKILL_STOCK_PREFIX + userRecode.getSkuId());
        if (StringUtils.isEmpty(skuIdExist)){
            //没有库存 通知其他兄弟节点
            this.redisTemplate.convertAndSend("seckillpush",userRecode.getSkuId()+":0");
            return;
        }
        //保存用户的预下单数据
        OrderRecode orderRecode = new OrderRecode();
        orderRecode.setUserId(userRecode.getUserId());
        orderRecode.setNum(1);
        orderRecode.setSeckillGoods(this.getSeckillGoods(userRecode.getSkuId()));
        orderRecode.setOrderStr(MD5.encrypt(userRecode.getUserId()+userRecode.getSkuId()));
        //存储的redis中 数据类型hash
        this.redisTemplate.opsForHash().put(RedisConst.SECKILL_ORDERS,userRecode.getUserId(),orderRecode);
        //更新redis - stockCount，mySql - stockCount(剩余数量,库存)
        //异步 发送消息
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_SECKILL_STOCK, MqConst.ROUTING_SECKILL_STOCK, userRecode.getSkuId());
    }

    //更新秒杀存方法
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStock(Long skuId) {
        //更新缓存  从redis获取数据
        SeckillGoods seckillGoodsRedis = (SeckillGoods) this.redisTemplate.opsForHash().get(RedisConst.SECKILL_GOODS, skuId.toString());

//        SeckillGoods seckillGoods = new SeckillGoods();
        //获取剩余库存
        Long count = this.redisTemplate.opsForList().size(RedisConst.SECKILL_STOCK_PREFIX + skuId);
        //设置剩余库存数量
//        seckillGoods.setStockCount(count.intValue());
//        seckillGoods.setUpdateTime(new Date());
        seckillGoodsRedis.setStockCount(count.intValue());
        seckillGoodsRedis.setUpdateTime(new Date());
//        UpdateWrapper<SeckillGoods> seckillGoodsUpdateWrapper = new UpdateWrapper<>();
//        seckillGoodsUpdateWrapper.eq("sku_id",skuId);
//        this.seckillGoodsMapper.update(seckillGoods,seckillGoodsUpdateWrapper);
        //修改数据库
        this.seckillGoodsMapper.updateById(seckillGoodsRedis);
        //修改缓存
        this.redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS,skuId.toString(),seckillGoodsRedis);
    }

    @Override
    public Result checkOrder(Long skuId, String userId) {
        /**
         * 1.验证缓存中是否有用户Id
         * 2.判断用户是否有预下单  有-->抢购成功 去下单
         * 3.判断用户是否真正下过订单  --> 抢购成功 我的订单
         * 4.校验一下状态位
         */
        String userKey = RedisConst.SECKILL_USER+userId;
        Boolean exist = this.redisTemplate.hasKey(userKey);
        //exist = true 说明有用户
        if (exist){
            //有用户 判断预下单记录
            Boolean result = this.redisTemplate.opsForHash().hasKey(RedisConst.SECKILL_ORDERS, userId);
            //result = true 说明有预下单数据
            if (result){
                OrderRecode orderRecode = (OrderRecode) this.redisTemplate.opsForHash().get(RedisConst.SECKILL_ORDERS, userId);
                return Result.build(orderRecode, ResultCodeEnum.SECKILL_SUCCESS);
            }
        }
        //判断用户是否真正下过订单 订单结算点击提交时 保存到数据库 同时还保存到缓存
        Boolean flag = this.redisTemplate.opsForHash().hasKey(RedisConst.SECKILL_ORDERS_USERS, userId);
        //flag = true 说明下过订单 提示 我的订单
        if (flag){
            String orderId = (String) this.redisTemplate.opsForHash().get(RedisConst.SECKILL_ORDERS_USERS, userId);
            return Result.build(orderId,ResultCodeEnum.SECKILL_ORDER_SUCCESS);
        }
        //判断状态位
        String state = (String) CacheHelper.get(skuId.toString());
        if ("0".equals(state)||StringUtils.isEmpty(state)){
            return Result.build(null,ResultCodeEnum.SECKILL_FAIL);
        }
        return Result.build(null,ResultCodeEnum.SECKILL_RUN);
    }
}
